An interpreter corresponds to a program that executes other programs. In contrast, a compiler does not execute its input program (i.e., the “source code”); rather, the compiler translates the input program into executable “machine code” (also known as “object code”) that is subsequently output to a file for later execution. In some instances, it may be possible to execute a particular piece of source code either directly by an interpreter or by compiling it and then executing the resulting machine code. A program executed using an interpreter is typically slower than a program executed using machine code. The difference in execution performance is directly related to the operation of the interpreter. Specifically, the interpreter analyzes each statement that is encountered and performs the action(s) specified in the statement.
With respect to analyzing each statement in the program, the interpreter must determine what each expression and variable within the statement means in order to determine what action to perform. If the statement includes a variable, then the interpreter must determine the value associated with the variable. Said another way, the interpreter must determine the value bound to the variable. The binding of a variable to a value is commonly referred to as “variable assignment.” Because the program may include a number of variables with the same name, the interpreter typically uses a scoping rule to determine the specific variable being referred to in the statement it is interpreting. Each variable in the program is associated with the scope that defines the portion of the program in which the particular variable may be accessed by name.
The scoping rules allow the interpreter to unambiguously select the appropriate variable (assuming that there are multiple variables with the same name) at any point in the execution of the program. There are two general scoping rule paradigms: (i) lexical scoping rules and (ii) dynamic scoping rules. In general, the lexical scoping rules limit the scope of a particular variable to the portion of the source code in which the variable was initially declared. In contrast, dynamic scoping rules define the scope of the particular variable based on the nesting of procedure and function calls at runtime.
Regardless of the implementation of the scoping rules of a given interpreter, the interpreter only applies the scoping rules to the variable bindings defined by the interpreter. Thus, if a given application involves a communication between a first interpreter and a second interpreter (i.e., a first interpreter calls a second interpreter), then each interpreter will maintain its own variable bindings and these variable bindings will not be accessible to the other interpreter. Thus, a variable defined by one of the interpreters is not understood by the second interpreter because the variable binding is outside the scope of the second interpreter.
Accordingly, when a first interpreter issues a call to a second interpreter, the second interpreter executes the call and sends the resulting data back to the first interpreter. If the first interpreter wants the second interpreter to further process the resulting data, then the first interpreter sends the resulting data (i.e., the data just received from the second interpreter) back to the second interpreter for processing. In order to send the resulting data from the second interpreter to the first interpreter (and vise versa), the resulting data must typically be serialized (and subsequently de-serialized when it arrives at its destination). Because each interpreter maintains its own variable bindings, a variable binding for the resulting data (i.e., a variable assignment that is bound to the resulting data) must be maintained by both the first interpreter and the second interpreter.